//
// Created by lostleaf on 2020/5/23.
// oes 通用辅助对象及函数封装

#pragma once
#include <codecvt>
#include <condition_variable>
#include <iostream>
#include <locale>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <algorithm>

#include <pybind11/pybind11.h>

namespace py = pybind11;

//任务结构体
struct TdTask {
  OesApiSessionInfoT *pSessionInfo = nullptr;
  SMsgHeadT *pMsgHead = nullptr;
  OesRspMsgBodyT *pMsgBody = nullptr;
};

class TerminatedError : std::exception {};

template <class TaskType> class TaskQueue {
private:
  std::queue<TaskType> queue_;   //标准库队列
  std::mutex mutex_;             //互斥锁
  std::condition_variable cond_; //条件变量

  bool _terminate = false;

public:
  //存入新的任务
  void push(const TaskType &task) {
    std::unique_lock<std::mutex> mlock(mutex_);
    queue_.push(task);  //向队列中存入数据
    mlock.unlock();     //释放锁
    cond_.notify_one(); //通知正在阻塞等待的线程
  }

  //取出老的任务
  TaskType pop() {
    std::unique_lock<std::mutex> mlock(mutex_);
    cond_.wait(mlock, [&]() {
      return !queue_.empty() || _terminate;
    }); //等待条件变量通知
    if (_terminate)
      throw TerminatedError();
    TaskType task = queue_.front(); //获取队列中的最后一个任务
    queue_.pop();                   //删除该任务
    return task;                    //返回该任务
  }

  void terminate() {
    _terminate = true;
    cond_.notify_all(); //通知正在阻塞等待的线程
  }
};

//从字典中获取某个键值对应的基础类型，并赋值到请求结构体对象的值上
template <class T> void getValue(const py::dict &d, const char *key, T &value) {
  if (d.contains(key)) //检查字典中是否存在该键值
  {
    py::object o = d[key]; //获取该键值
    value = o.cast<T>();
  }
}

//从字典中获取某个键值对应的数组，并赋值到请求结构体对象的值上
template <class T>
void getArray(const pybind11::dict &d, const char *key, T *value, size_t num) {
  if (d.contains(key)) {
    auto o = d[key];
    auto v = o.cast<std::vector<T>>();
    std::copy_n(v.begin(), std::min(v.size(), num), value);
  }
}

//模板特化，处理字符串
template <>
void getArray(const pybind11::dict &d, const char *key, char *value,
               size_t num) {
  if (d.contains(key)) {
    py::object o = d[key];
    std::string s = o.cast<std::string>();
    strncpy(value, s.c_str(), num);
  }
}